import asyncio
import pydash
import httpx

from common import *
from metadbconn import *
from videoobj import *

_semaphore = asyncio.Semaphore(CONFIG["async_coroutines"])


# ================基础路径================
def get_media_content_dirs(cover_old_nfo=False, ignore_GHS=False):
    media_root = sorted(Path(i) for i in pydash.get(CONFIG, "mode.common.media_root"))
    types = set(pydash.get(CONFIG, "mode.common.types_on_disk"))
    if ignore_GHS:
        types = types - SENSITIVE_WORDS
    media_catagories = sorted((i / j).resolve() for j in types for i in media_root)
    for i in media_catagories:
        i.mkdir(parents=True, exist_ok=True)
    media_dirs = sorted(pydash.flatten(tuple(j for j in i.glob("*") if j.is_dir()) for i in media_catagories))
    if not (DEBUG or cover_old_nfo):
        # movie或者TV文件夹里有nfo文件则不刷新
        media_dirs = [i for i in media_dirs if not next(i.glob("*.nfo"), None)]
    return media_dirs


# ================构造videoobj对象================
async def construct_videoobjs(metadb_conns):
    """
    :return: movieobjs, tvobjs
    """
    movieobjs: list[Movie] = []
    tvobjs: list[TV] = []
    for media_dir in get_media_content_dirs():
        myobj, flag = await construct_videoobj(metadb_conns, media_dir)
        if flag != F_OK:
            continue
        if myobj.media_type == "tv":
            tvobjs.append(myobj)
        else:
            movieobjs.append(myobj)
    return movieobjs, tvobjs


async def construct_videoobj(metadb_conns: dict[MetadbConn], media_dir: Path):
    sele = search_element_from_endpath(media_dir)
    lang = sele.lang or CONFIG["lang"]
    if sele.metaDB not in metadb_conns.keys() or sele.metaDB_ID is None:
        tmdb_conn: TmdbConn = metadb_conns["tmdb"]
        metaDB = "tmdb"
        media_name = filename_for_search(sele.name)
        try:
            metaDB_ID, flag = await tmdb_conn.search(
                media_name, sele.media_type, sele.year, lang, CONFIG["unsafe_search"]
            )
            if flag != F_OK:
                return None, flag
        except httpx.ConnectError as e:
            METADB_LOGGER.error("在tmdb搜索{}“{}”时出现网络错误".format(sele.media_type, media_dir.name))
            return None, F_SKIP
    else:
        metaDB, metaDB_ID = sele.metaDB, sele.metaDB_ID
    # ================
    metadb_conn = metadb_conns[metaDB]
    if sele.media_type == "movie":
        return Movie(media_dir, metaDB, metaDB_ID, lang, metadb_conn), F_OK
    else:
        return TV(media_dir, metaDB, metaDB_ID, lang, metadb_conn), F_OK


async def output_season(season: Season):
    try:
        await season.rename_purifying()
        await season.output_jellyfin_nfo()
        await season.output_jellyfin_episode_nfos()
    except httpx.ConnectError as e:
        METADB_LOGGER.error(
            "在{}获取TV“{}”的S{}的数据时出现网络错误".format(
                season.metaDB, season.parent.path.name, season.season_number
            )
        )
    except Exception as e:
        VOBJ_LOGGER.error(
            "TV“{}”的S{}对视频文件和字幕的名称规范化失败".format(season.parent.path.name, season.season_number)
        )
        VOBJ_LOGGER.error(e)


async def output_tv(tvobj: TV):
    try:
        await tvobj.discover_seasons()
        season_coroutines = [output_season(s) for s in tvobj.seasons.values()]
        await asyncio.gather(*season_coroutines)
        await tvobj.output_jellyfin_nfo()
    except httpx.ConnectError as e:
        METADB_LOGGER.error("在{}获取TV“{}”的数据时出现网络错误".format(tvobj.metaDB, tvobj.path.name))


async def output_movie(movieobj: Movie):
    try:
        await movieobj.output_jellyfin_nfo()
    except httpx.ConnectError as e:
        METADB_LOGGER.error("在{}获取movie“{}”的数据时出现网络错误".format(movieobj.metaDB, movieobj.path.name))


async def output_jellyfin_nfo(movieobjs: list[Movie], tvobjs: list[TV]):
    coroutines = [output_movie(i) for i in movieobjs] + [output_tv(j) for j in tvobjs]
    async with _semaphore:
        await asyncio.gather(*coroutines)


# ================从nfo构造videoobj对象================
def construct_videoobj_from_nfo(metadb_conns: dict[MetadbConn], media_dir: Path):
    sele = search_element_from_endpath(media_dir)
    lang = sele.lang or CONFIG["lang"]
    metainfo = metainfo_from_nfo(media_dir)
    if metainfo is None:
        return None, F_SKIP
    metaDB, metaDB_ID = metainfo.metaDB, metainfo.metaDB_ID
    if metaDB is None or metaDB_ID is None:
        return None, F_SKIP
    metadb_conn = metadb_conns[metaDB]
    if sele.media_type == "movie":
        return Movie(media_dir, metaDB, metaDB_ID, lang, metadb_conn), F_OK
    else:
        return TV(media_dir, metaDB, metaDB_ID, lang, metadb_conn), F_OK


def construct_videoobjs_from_nfo(metadb_conns):
    """
    :return: movieobjs, tvobjs
    """
    movieobjs: list[Movie] = []
    tvobjs: list[TV] = []
    for media_dir in get_media_content_dirs(cover_old_nfo=True, ignore_GHS=True):
        myobj, flag = construct_videoobj_from_nfo(metadb_conns, media_dir)
        if flag != F_OK:
            continue
        if myobj.media_type == "tv":
            tvobjs.append(myobj)
        else:
            movieobjs.append(myobj)
    return movieobjs, tvobjs


def output_volume_info(movieobjs: list[Movie], tvobjs: list[TV]):
    VOBJ_LOGGER.info("开始对磁盘上所有作品的文件夹名称进行规范化")
    for i in movieobjs:
        try:
            i.rename_purifying()
        except Exception as e:
            VOBJ_LOGGER.error("本地文件夹{}对名称规范化失败".format(i.path.name))
            VOBJ_LOGGER.error(e)
    for j in tvobjs:
        try:
            j.rename_purifying()
        except Exception as e:
            VOBJ_LOGGER.error("本地文件夹{}对名称规范化失败".format(j.path.name))
            VOBJ_LOGGER.error(e)
    VOBJ_LOGGER.info("开始统计磁盘上所有作品的视频文件体积，输出文件在output文件夹中")
    Path("output").mkdir(exist_ok=True)
    write_csv(
        Path("output/media_volume.csv"),
        [
            [
                "media_name",
                "media_path",
                "metaDB",
                "metaDB_ID",
                "media_type",
                "video_count",
                "video_volume (MiB)",
                "total_volume (MiB)",
            ]
        ]
        + [i.calc_volume_info() for i in movieobjs]
        + [j.calc_volume_info() for j in tvobjs],
    )


# ================main================
async def work(metadb_conns):
    movieobjs, tvobjs = await construct_videoobjs(metadb_conns)
    await output_jellyfin_nfo(movieobjs, tvobjs)
    VOBJ_LOGGER.info("================")
    movieobjs, tvobjs = construct_videoobjs_from_nfo(metadb_conns)
    output_volume_info(movieobjs, tvobjs)
